﻿//_______________________________________________________________________________________________________________________
// Copy Right (c) 2010, 2011 SapiensIT Consulting GmbH, all rights reserved
//_______________________________________________________________________________________________________________________
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT
// NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
//_______________________________________________________________________________________________________________________
// PROJECT:     sapiens.at.SharePoint (SharePoint 2010/V4)
//_______________________________________________________________________________________________________________________
// VERSION:     2.0
//_______________________________________________________________________________________________________________________
// DESCRIPTION: 
//_______________________________________________________________________________________________________________________
// LICENSE:     Different licens Types apply to different file types:
//               - Source code files are governed by the MICROSOFT PUBLIC LICENSE (Ms-PL)
//               - Binary files are governed by MSDN CODE GALLERY BINARY LICENSE
//               - Documentation files are governed by CREATIVE COMMONS ATTRIBUTION 3.0 LICENSE
//
//               The licenses can be found at the projects source location.
//               If you do not agree, don't use it!
// 
//_______________________________________________________________________________________________________________________
// Start SharePoint Branding, visit http://www.cliggin.com! Visit us on http://www.sapiens.at! Tell your friends! 
//_______________________________________________________________________________________________________________________
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint;
using Sapiens.at.SharePoint.Entities;
using Sapiens.at.SharePoint.Utils;
using QB = Sapiens.at.SharePoint.Utils.QueryBuilder;
using Sapiens.at.Core.Utils;

namespace Sapiens.at.SharePoint.Repositories
{
    /// <summary>
    /// A wrapper, providing easy access to methods concerning a <see cref="SPList"/>.
    /// </summary>
    public class ListRepository : IListRepository
    {
        readonly SPList _list;
        /// <summary>
        /// Gets the SPList that this Repository wraps
        /// </summary>
        public SPList List 
        {
            get { return _list; }
        }

        /// <summary>
        /// Gets the SPWeb that this list is in
        /// </summary>
        public SPWeb Web
        {
            get { return List.Lists.Web; }
        }

        /// <summary>
        /// Creates a new instance of Repository.
        /// </summary>
        /// <param name="id">The id of the list</param>
        /// <param name="web">The web the list is in</param>
        public ListRepository(Guid id, SPWeb web)
        {
            _list = web.Lists[id];
        }

        /// <summary>
        /// Creates a new instance of Repository.
        /// </summary>
        /// <param name="list">The list to wrap</param>
        public ListRepository(SPList list) : this(list.ID, list.Lists.Web) { }

        /// <summary>
        /// Creates a new instance of Repository.
        /// </summary>
        /// <param name="webRelativeUrl">The URL of the list relative to the <paramref name="web"/></param>
        /// <param name="web">The web the list is in</param>
        public ListRepository(string webRelativeUrl, SPWeb web)
        {
            _list = web.GetList(string.Format("{0}/{1}", web.Url, webRelativeUrl));
        }

        /// <summary>
        /// Creates a new ListItem for this list and returns it as an Entity. NOTE: The ListItem is not yet saved. Use <see cref="IEntity.Update()"/>
        /// or <see cref="IEntity.SystemUpdate()"/> to do so.
        /// </summary>
        /// <remarks>
        /// The method tries to get the <see cref="SPContentTypeId"/> from the <typeparamref name="TEntity"/> using the static field 'CTypeId'.
        /// If this field is not available it throws a TargetException. Implement the field using
        ///     <code>public static readonly SPContentTypeId CTypeId = new SPContentTypeId(CTYPE_ID);</code>
        /// or use another overriden method of <see cref="Create{TEntity}()"/> instead.
        /// </remarks>
        /// <typeparam name="TEntity">The entity type to return</typeparam>
        /// <returns>A new ListItem with the contenttype specified in the proper Entity class</returns>
        public TEntity Create<TEntity>()
            where TEntity : IEntity
        {
            return Create<TEntity>(ListItemMapper.GetContentTypeId<TEntity>());
        }

        /// <summary>
        /// Creates a new ListItem for this list and returns it as an Entity. NOTE: The ListItem is not yet saved. Use <see cref="IEntity.Update()"/>
        /// or <see cref="IEntity.SystemUpdate()"/> to do so.
        /// </summary>
        /// <typeparam name="TEntity">The entity type to return</typeparam>
        /// <param name="cTypeId">The contenttype to assign to the new listitem</param>
        /// <returns>A new ListItem with the specified contenttype</returns>
        public TEntity Create<TEntity>(SPContentTypeId cTypeId)
            where TEntity : IEntity
        {
            return Create<TEntity>(cTypeId, null, SPFileSystemObjectType.File);
        }

        /// <summary>
        /// Creates a new ListItem for this list and returns it as an Entity. NOTE: The ListItem is not yet saved. Use <see cref="IEntity.Update()"/>
        /// or <see cref="IEntity.SystemUpdate()"/> to do so.
        /// </summary>
        /// <typeparam name="TEntity">The entity type to return</typeparam>
        /// <param name="cTypeId">The contenttype to assign to the new listitem</param>
        /// <param name="parent">If not null, the new listitem is created inside the folder of this listitem</param>
        /// <param name="objectType">The object type this listitem represents</param>
        /// <returns>A new ListItem with the specified contenttype</returns>
        public TEntity Create<TEntity>(SPContentTypeId cTypeId, SPListItem parent, SPFileSystemObjectType objectType)
            where TEntity : IEntity
        {
            return Create<TEntity>(cTypeId, parent, objectType, null);
        }
        /// <summary>
        /// Creates a new ListItem for this list and returns it as an Entity. NOTE: The ListItem is not yet saved. Use <see cref="IEntity.Update()"/>
        /// or <see cref="IEntity.SystemUpdate()"/> to do so.
        /// </summary>
        /// <typeparam name="TEntity">The entity type to return</typeparam>
        /// <param name="cTypeId">The contenttype to assign to the new listitem</param>
        /// <param name="parent">If not null, the new listitem is created inside the folder of this listitem</param>
        /// <param name="objectType">The object type this listitem represents</param>
        /// <param name="FileLeafRef">The Name of the new entity</param>
        /// <returns>A new ListItem with the specified contenttype</returns>
        public TEntity Create<TEntity>(SPContentTypeId cTypeId, SPListItem parent, SPFileSystemObjectType objectType, string FileLeafRef)
            where TEntity : IEntity
        {
            Validation.ArgumentNotNull(cTypeId, "cTypeId");

            // create the listitem
            var listItem = (string.IsNullOrEmpty(FileLeafRef)) ?
                List.Items.Add(parent == null ? List.RootFolder.ServerRelativeUrl : parent.Folder.ServerRelativeUrl, objectType) :
                List.Items.Add(parent == null ? List.RootFolder.ServerRelativeUrl : parent.Folder.ServerRelativeUrl, objectType, Tools.RemoveIllegalUrlCharacters(FileLeafRef));
            
            var cType = List.ContentTypes[List.ContentTypes.BestMatch(cTypeId)];
            listItem[SPBuiltInFieldId.ContentType] = cType.Name;
            listItem[SPBuiltInFieldId.ContentTypeId] = cType.Id;

            return ListItemMapper.GetEntity<TEntity>(listItem);
        }

        /// <summary>
        /// Finds an entity from the repository with its id.
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="id"></param>
        /// <returns></returns>
        public TEntity GetEntity<TEntity>(int id)
            where TEntity : IEntity
        {
            return ListItemMapper.GetEntity<TEntity>(List.GetItemById(id));
        }

        /// <summary>
        /// Gets all entities from the repository.
        /// </summary>
        /// <remarks>
        /// Uses the search scope "RecursiveAll".
        /// </remarks>
        /// <typeparam name="TEntity"></typeparam>
        /// <returns></returns>
        public IEnumerable<TEntity> GetEntities<TEntity>()
        {
            return GetEntities<TEntity>(SPViewScope.RecursiveAll);
        }

        /// <summary>
        /// Gets all entities from the repository using a search scope.
        /// </summary>
        /// <remarks>
        /// The scope can be "Recursive", "RecursiveAll"
        /// </remarks>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="scope"></param>
        /// <returns></returns>
        public IEnumerable<TEntity> GetEntities<TEntity>(SPViewScope scope)
        {
            var query = new SPQuery().Scope(scope);
            return GetEntities<TEntity>(query);
        }

        /// <summary>
        /// Gets all entities from the repository from a certain content type.
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="cTypeId"></param>
        /// <returns></returns>
        public IEnumerable<TEntity> GetEntities<TEntity>(SPContentTypeId cTypeId)
        {
            var query = new SPQuery()
                .Where(QB.IsContentType(cTypeId))
                .Scope(SPViewScope.RecursiveAll);
            return GetEntities<TEntity>(query);
        }

        /// <summary>
        /// Gets all entities from the repository matching the query.
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="query"></param>
        /// <returns></returns>
        public IEnumerable<TEntity> GetEntities<TEntity>(SPQuery query)
        {
            var entities = List.GetItems(query).Cast<SPListItem>();
            return entities.Select(e => (TEntity)Activator.CreateInstance(typeof(TEntity), e));
        }

        /// <summary>
        /// Adds a role for a user or group to the list.
        /// </summary>
        /// <param name="principal">The user or group</param>
        /// <param name="roleType">The type of role</param>
        public void AddRole(SPPrincipal principal, SPRoleType roleType)
        {
            var roleAssignment = new SPRoleAssignment(principal);
            var roleDefinition = Web.RoleDefinitions.GetByType(roleType);
            roleAssignment.RoleDefinitionBindings.Add(roleDefinition);
            List.RoleAssignments.Add(roleAssignment);
        }

        /// <summary>
        /// Breaks the role inheritance and removes all role assignments.
        /// </summary>
        public void ClearRoles()
        {
            List.BreakRoleInheritance(false);
            for (int counter = List.RoleAssignments.Count - 1; counter >= 0; counter--)
            {
                List.RoleAssignments.Remove(counter);
            }
        }
    }
}
